import os
import subprocess
from typing import List, Optional
from pathlib import Path

from django.conf import settings
from django.core.management.color import color_style


class TimerManager:
    TIMERS_DIR = Path.joinpath(settings.BASE_DIR, Path('00_script/timer/'))
    SYSTEMD_DIR = Path('/usr/lib/systemd/system/')

    def __init__(self):
        self.style = color_style(force_color=True)

    @staticmethod
    def execute_shell(command, check=True) -> subprocess.CompletedProcess:
        """执行Linux命令"""
        try:
            return subprocess.run(
                command,
                shell=True,
                check=check,    # 是否检查返回码；如果返回码非 0，抛出 subprocess.CalledProcessError
                text=True,      # 是否以文本模式处理输入输出。
                capture_output=True # 自动捕获输出和错误
            )
        except subprocess.CalledProcessError as e:
            raise e

    def get_timers_setting(self):
        jobs_settings = getattr(settings, 'TIMER_JOBS_NAME_LIST', [])
        if not jobs_settings:
            print(self.style.WARNING('Not set timers, Can set timer by settings.TIMER_JOBS_NAME_LIST = ["xxx"].'))
            return []

        return jobs_settings

    def get_setting_timer_names(self) -> List[str]:
        return self.get_timers_setting()

    def add_path_file(self, timer_name):
        """
        添加软连接文件，已存在强制覆盖
        """
        name = timer_name.split('.')[0]
        service_name = f'{name}.service'
        service_path = Path().joinpath(self.TIMERS_DIR, service_name)
        timer_path = Path().joinpath(self.TIMERS_DIR, timer_name)
        ln_service_path = self.SYSTEMD_DIR.joinpath(service_name)
        ln_timer_path = self.SYSTEMD_DIR.joinpath(timer_name)
        command = f'ln -sf {service_path} {ln_service_path}'
        command2 = f'ln -sf {timer_path} {ln_timer_path}'

        try:
            self.execute_shell(command=command)
            self.execute_shell(command=command2)
        except Exception as e:
            print(self.style.ERROR(f'添加软链接 {timer_name} 出现错误：{str(e)}'))
            return

        print(self.style.SUCCESS(f'New add timer: {timer_name}'))

    def remove_path_file(self, timer_name):
        """
        删除 linux 系统 timer

        返回错误的定时器名称
        """
        name = timer_name.split('.')[0]
        ln_service_path = self.SYSTEMD_DIR.joinpath(f'{name}.service')
        ln_timer_path = self.SYSTEMD_DIR.joinpath(timer_name)
        command = f'rm {ln_service_path} -f'
        command2 = f'rm {ln_timer_path} -f'

        if os.path.exists(command2.split(' ')[1]):
            try:
                self.execute_shell(command=command)
                self.execute_shell(command=command2)
            except Exception as e:
                print(self.style.ERROR(f'删除软链接 {timer_name} 出现错误：{str(e)}'))
                return timer_name

        return None

    def add_timers(self, timer_name: str = None):
        """
        添加定时器任务
        timer_name:xxx.timer
        """
        if not timer_name:
            # 添加所有定时任务
            task_list = self.get_setting_timer_names()
            for task in task_list:
                path = f'/usr/lib/systemd/system/{task}'
                if not os.path.exists(path):
                    # 文件不存在 添加
                    try:
                        self.add_path_file(timer_name=task)
                    except Exception as e:
                        pass
            return

        if not timer_name.endswith('.timer'):
            print(f'请指定定时器名称')
            return

        sys_started_timers = self.get_sys_started_timers()
        if sys_started_timers is None:
            return

        if timer_name not in sys_started_timers:
            # 添加timer
            try:
                self.add_path_file(timer_name=timer_name)
            except Exception as e:
                pass

    def remove_timers(self, timer_name: str = None):
        """
        删除定时器任务
        timer_name： 服务名称 xxx.timer
        """
        rm_ok_timers = []
        rm_err_timers = []

        sys_started_timers = self.get_sys_started_timers()
        if sys_started_timers is None:
            return

        if timer_name:
            if not timer_name.endswith('.timer'):
                print(f'定时器名称无效，格式为：xx.timer')
                return

            need_remove_timers = [timer_name]
        else:
            need_remove_timers = self.get_setting_timer_names()

        for name in need_remove_timers:
            if name in sys_started_timers:
                self.stop_timers(timer_name=name)

            err_timer = self.remove_path_file(timer_name=name)
            if err_timer:
                rm_err_timers.append(err_timer)
            else:
                rm_ok_timers.append(name)

        if rm_err_timers:
            print(self.style.ERROR('>>>>>> Error remove timers:'))
            for t in rm_err_timers:
                print(t)

            print(self.style.ERROR('<<<<<<<\n'))

        if rm_ok_timers:
            print(self.style.SUCCESS('>>>>>> Removed timers:'))
            for t in rm_ok_timers:
                print(t)

            print(self.style.SUCCESS('<<<<<<<\n'))

    def show_timers(self):
        setting_timer_names = self.get_setting_timer_names()
        started_timers = []
        not_start_timers = []
        sys_started_timers = self.get_sys_started_timers()
        if sys_started_timers is None:
            return

        for name in setting_timer_names:
            if name in sys_started_timers:
                started_timers.append(name)
            else:
                not_start_timers.append(name)

        is_enabled_dict = self.get_timers_is_enabled_status(timer_names=setting_timer_names)

        if started_timers:
            print(self.style.SUCCESS('>>>>>> Running timers:'))
            for t in started_timers:
                print(f'{t}  [{is_enabled_dict[t]}]')

            print(self.style.SUCCESS('<<<<<<<\n'))

        if not_start_timers:
            print(self.style.WARNING('>>>>>> Not Start timers:'))
            for t in not_start_timers:
                print(f'{t}  [{is_enabled_dict[t]}]')

            print(self.style.WARNING('<<<<<<<\n'))

    def get_is_enabled_timer(self, timer_name: str) -> str:
        code_map = {
            0: 'enabled',
            1: 'disabled',
            2: 'static或不可管理',
            3: 'indirect',
            4: 'not found',
        }
        try:
            output = self.execute_shell(f"systemctl is-enabled {timer_name}", check=False)
            return code_map.get(output.returncode, 'unknown')
        except subprocess.CalledProcessError as e:
            print(self.style.ERROR(f'get is-enabled error：{str(e)}'))
            return 'unknown'

    def get_timers_is_enabled_status(self, timer_names: list[str]) -> dict:
        timers_status = {}
        for name in timer_names:
            output = self.get_is_enabled_timer(timer_name=name)
            if output:
                timers_status[name] = output
            else:
                timers_status[name] = 'unkown'

        return timers_status

    def start_timers(self, timer_name: str):
        """
        运行定时器任务
        """

        if not timer_name or not timer_name.endswith('.timer'):
            print('请输入需要运行的定时器名称，xxx.timer')
            return

        path = f'/usr/lib/systemd/system/{timer_name}'

        if not os.path.exists(path):
            print('请使用添加（add）命令，先添加定时器，然后才能启动定时器。')
            return

        command = f'systemctl start {timer_name}'

        try:
            self.execute_shell(command=command)
        except Exception as e:
            print(self.style.ERROR(f'定时器 {timer_name} 出现错误：{str(e)}'))
            return

        print(self.style.SUCCESS(f'Start timer: {timer_name}'))

    def stop_one_timer(self, timer_name: str):
        """停止一个定时器任务"""

        if not timer_name or not timer_name.endswith('.timer'):
            print(f'请填写需要停止的定时器名称')
            return

        command = f'systemctl stop {timer_name}'

        try:
            self.execute_shell(command=command)
        except Exception as e:
            print(self.style.ERROR(f'定时器 {timer_name} 出现错误：{str(e)}'))
            return

        print(self.style.SUCCESS(f'Stop timer: {timer_name}'))

    def stop_timers(self, timer_name: str = None):
        """
        停止定时器任务
        """

        if not timer_name:
            setting_timer_names = self.get_setting_timer_names()
            for task in setting_timer_names:
                path = f'/usr/lib/systemd/system/{task}'
                if os.path.exists(path):
                    self.stop_one_timer(timer_name=task)
            return

        path = f'/usr/lib/systemd/system/{timer_name}'
        if os.path.exists(path):
            self.stop_one_timer(timer_name=timer_name)

    def get_sys_started_timers(self) -> Optional[List[str]]:
        """
        查询系统中所有已启动的定时器名称

        :return:
            List[str]   # success
            None        # error
        """
        try:
            output = self.execute_shell(
                command="""systemctl list-units --type=timer --all --no-pager | awk '$3 == "active"' | awk '{print $1}' | grep '.timer'""")
            return [s for s in output.stdout.splitlines() if s]
        except Exception as e:
            print(self.style.ERROR(f'List all started timers error：{str(e)}'))
            return None

    def enable_one_timer(self, timer_name: str) -> Optional[subprocess.CompletedProcess]:
        try:
            output = self.execute_shell(
                command=f"systemctl enable {timer_name}", check=False)
            if output.returncode != 0:
                print(self.style.ERROR(f'enable timer error：{output.stderr}'))
                return None

            return output
        except Exception as e:
            print(self.style.ERROR(f'enable timer error：{str(e)}'))
            return None

    def enable_timers(self, timer_name: str = None):
        """
        开启开机自启动
        """
        setting_timer_names = self.get_setting_timer_names()
        if timer_name:
            if timer_name not in setting_timer_names:
                print(self.style.ERROR('无效的定时器名称，不在本服务的定时器列表中。'))
                return

            output = self.enable_one_timer(timer_name=timer_name)
            if output is not None:
                print(self.style.SUCCESS(f'成功开启开机自启动（enable）定时器 “{timer_name}”。'))

            return

        started_timers = []
        not_start_timers = []
        sys_started_timers = self.get_sys_started_timers()
        for name in setting_timer_names:
            if name in sys_started_timers:
                started_timers.append(name)
            else:
                not_start_timers.append(name)

        if not started_timers:
            print(self.style.WARNING('没有“已启动”的定时器。未指定定时器名称时，只对“已启动”状态的所有定时器开启开机自启动。'))
            return

        for name in started_timers:
            output = self.enable_one_timer(timer_name=name)
            if output is not None:
                print(self.style.SUCCESS(f'成功开机自启动（enable）定时器 “{timer_name}”。'))

        self.show_timers()

    def disable_one_timer(self, timer_name: str):
        try:
            output = self.execute_shell(command=f"systemctl disable {timer_name}", check=False)
            if output.returncode != 0:
                print(self.style.ERROR(f'enable timer error：{output.stderr}'))
                return None

            return output
        except Exception as e:
            print(self.style.ERROR(f'disable timer error：{str(e)}'))
            return None

    def disable_timers(self, timer_name: str = None):
        """
        关闭开机自启动
        """
        setting_timer_names = self.get_setting_timer_names()
        if timer_name:
            if timer_name not in setting_timer_names:
                print(self.style.ERROR('无效的定时器名称，不在本服务的定时器列表中。'))
                return

            output = self.disable_one_timer(timer_name=timer_name)
            if output is not None:
                print(self.style.SUCCESS(f'关闭开机自启动（disable）定时器 “{timer_name}”。'))

            return

        started_timers = []
        not_start_timers = []
        sys_started_timers = self.get_sys_started_timers()
        for name in setting_timer_names:
            if name in sys_started_timers:
                started_timers.append(name)
            else:
                not_start_timers.append(name)

        if not started_timers:
            print(self.style.WARNING(
                '没有“已启动”的定时器。未指定定时器名称时，只对“已启动”状态的所有定时器开启开机自启动。'))
            return

        for name in started_timers:
            output = self.disable_one_timer(timer_name=name)
            if output is not None:
                print(self.style.SUCCESS(f'成功关闭开机自启动（disable）定时器 “{timer_name}”。'))

        self.show_timers()
